//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
/*
 * Stubs driver Copyright 1993 by David Wexelblat <dwex@goblin.org>
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of David Wexelblat not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  David Wexelblat makes no representations
 * about the suitability of this software for any purpose.  It is provided
 * "as is" without express or implied warranty.
 *
 * DAVID WEXELBLAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL DAVID WEXELBLAT BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Generic VGA 320x200x256 driver developed from stub driver.
 *
 * Limited to 320x200 physical and virtual resolution, 70 Hz refresh.
 * Should work on any 100% VGA compatible card (SVGA 132 column textmode
 * may break it on some cards).
 * This is a bit of a joke, but it doesn't look too bad, and a good virtual
 * window manager with mouse-push paging like fvwm works nicely. Should be
 * useful for playing Doom.
 *
 * Requires a mode with a 25 MHz clock to be defined. The timing doesn't
 * matter, it is hardcoded in the driver. Example mode line:
 *
 *  "320x200"     25      320  344  376  400    200  204  206  225
 *
 * In SVGA timing terms, it is actually more of a 12.5 MHz dot clock 320x400
 * mode with each of 200 scanlines displayed twice.
 *
 * It doesn't use the mode timing abstractions used by the other drivers;
 * standard VGA 320x200x256 is a very weird mode. There's not much potential
 * for flexibility in modes anyway since the amount of addressable video
 * memory is limited to 64K. Note that CRTC[9], bit 7 (scanline doubling) is
 * not set, contrary to what one might expect; setting this bit would be
 * useful for supporting very low (200/240) vertical resolution modes in the
 * SVGA drivers (together with a true 12.5 MHz clock).
 *
 * Harm Hanemaayer <hhanemaa@cs.ruu.nl>
 */
#include <ddk.h>
#include "X.h"
#include "Xmd.h"
#include "compiler.h"
#include "xf86.h"
#include "xf86Priv.h"
#include "xf86Procs.h"
#include "xf86_OSlib.h"
#include "xf86_Config.h"
#include "gdivga.h"
#include "xf86_pci.h"
#include "vgamisc.h"

extern void vgaSetScreenInitHook(Boolean (* ChipScrInit)());

typedef struct {
    vgaHWRec std;               /* good old IBM VGA */
} vgaGenericRec, *vgaGenericPtr;

static Boolean     GenericProbe();
static char *   GenericIdent(int);
//static Boolean     GenericClockSelect(int);//not used
static void     GenericEnterLeave(Boolean);
static Boolean     GenericInit(DisplayModePtr mode);
static int      GenericValidMode(DisplayModePtr mode, Boolean verbose, int flag);
static void *   GenericSave(void *lpSave);
static void     GenericRestore(void *lpRes);
static void     GenericFbInit();
static void     GenericAdjust(int x, int y);

static DisplayModeRec Mode320x200 = {
    &Mode320x200,
    &Mode320x200,
    "320x200",
    0,    /* Clock index */
    320,
    344,
    376,
    400,
    200,
    204,
    206,
    225,
    0,
    0,
    320,
    344,
    376,
    400,
    200,
    204,
    206,
    225,
    FALSE,
    FALSE,
};

extern int vgaBitsPerPixel;
vgaVideoChipRec GENERIC = {
    /*
     * Function pointers
     */
    GenericProbe,
    GenericIdent,
    GenericEnterLeave,
    GenericInit,
    GenericValidMode,
    GenericSave,
    GenericRestore,
    GenericAdjust,
    vgaHWSaveScreen,
    (void (*)())NoopDDA,
    //(void (*)())NoopDDA,
    GenericFbInit,
    (void (*)())NoopDDA,    /* No banking. */
    (void (*)())NoopDDA,
    (void (*)())NoopDDA,
    0x10000,    /* 64K VGA window. */
    0x10000,
    16,
    0xFFFF,
    0x00000, 0x10000,
    0x00000, 0x10000,
    TRUE,        /* We have seperate read and write banks in a */
                 /* funny sort of way (just one bank). */
    VGA_NO_DIVIDE_VERT,
    {0,},
    8,
    FALSE,
    0,
    0,
    FALSE,
    FALSE,
    FALSE,
    &Mode320x200,
//    NULL,
    1,                         /* ChipClockMulFactor */
    1                          /* ChipClockDivFactor */
};

/* These are the fixed 100% VGA compatible CRTC register values used. */
#if 0
static unsigned char vga320x200x256_CRTC[24] = {
    0x5F,0x4F,0x50,0x82,0x54,0x80,0xBF,0x1F,0x00,0x41,0x00,0x00,
    0x00,0x00,0x00,0x00,0x9C,0x8E,0x8F,0x28,0x40,0x96,0xB9,0xA3
};
#else
static unsigned char vga320x200x256_CRTC[24] = {
       0x5F, 0x4F, 0x4F, 0x80, 0x54, 0x00, 0xBE, 0x1F,
       0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x9C, 0x0E, 0x8F, 0x28, 0x40, 0x8F, 0xBF, 0xA3
};
#endif

#define new ((vgaGenericPtr)vgaNewVideoState)

static char *
GenericIdent(int n)
{
    static char *chipsets[] = {"generic"};

    if (n + 1 > (int)(sizeof(chipsets) / sizeof(char *))) {
        return(NULL);
    }
    else {
        return(chipsets[n]);
    }
}

#define CLK_REG_SAVE           -1
#define CLK_REG_RESTORE        -2
#if 0//not used
static Boolean
GenericClockSelect(int no)
{
    static unsigned char save1;//, save2;
    unsigned char temp;

    switch(no)
    {
    case CLK_REG_SAVE:
        save1 = Inb(0x3CC);
#if 0
        /* SR2 holds the 'Dot Clock / 2' bit. */
        Outb(0x3c4, 0x01);
        save2 = Inb(0x3c5);
#endif
        break;
    case CLK_REG_RESTORE:
        Outb(0x3C2, save1);
#if 0
        Outw(0x3c4, 0x01 + ((unsigned)save2 << 8));
#endif
        break;
    default:
        /* We only support one clock, 25 MHz. */
        /* Always select clock 0 (25 MHz). */
        temp = Inb(0x3CC);
        Outb(0x3C2, (temp & 0xF3));
#if 0
        /* Now set bit 3 of SR1 (Divide dot clock by 2). */
        Outb(0x3c4, 0x01);
        temp = Inb(0x3c5);
        Outb(0x3c4, temp | 0x08);
#endif
    }
    return(TRUE);
}
#endif

static Boolean
GenericProbe()
{
//    unsigned char temp1, temp2;
    xf86ClearIOPortList(SvgaInfoRec.scrnIndex);
    xf86AddIOPorts(SvgaInfoRec.scrnIndex, Num_VGA_IOPorts, VGA_IOPorts);

    /*
     * First we attempt to figure out if one of the supported chipsets
     * is present.
     */
    if (SvgaInfoRec.chipset) {
        if (StrCaseCmp(SvgaInfoRec.chipset, GenericIdent(0)))
            return (FALSE);
        else
            GenericEnterLeave(ENTER);
        }
    else {
        unsigned char temp, origVal, newVal;
        GenericEnterLeave(ENTER);

        /*
        * Check if there is a VGA.  VGA has one more attribute register
        * than EGA, so see if we can read/write it.
        */
        temp = Inb(vgaIOBase + 0x0A); /* reset ATC flip-flop */
        Outb(0x3C0, 0x14 | 0x20); origVal = Inb(0x3C1);
        Outb(0x3C0, origVal ^ 0x0F);
        Outb(0x3C0, 0x14 | 0x20); newVal = Inb(0x3C1);
        Outb(0x3C0, origVal);
        if (newVal != (origVal ^ 0x0F)) {
            GenericEnterLeave(LEAVE);
            return(FALSE);
        }
    }

    if (!SvgaInfoRec.videoRam) {
        SvgaInfoRec.videoRam = 64;
    }

    if (!SvgaInfoRec.clocks) {
        SvgaInfoRec.clocks = 1;
        SvgaInfoRec.clock[0] = 25180;
    }

    SvgaInfoRec.maxClock = 25200;
    SvgaInfoRec.chipset = GenericIdent(0);
    SvgaInfoRec.bankedMono = FALSE;

    GENERIC.ChipUseLinearAddressing = TRUE;
    GENERIC.ChipLinearSize = SvgaInfoRec.videoRam * 1024;
    GENERIC.ChipLinearBase = 0xA0000;

    return (TRUE);
}


static void
GenericEnterLeave(Boolean enter)
{
    unsigned char temp;

    if (enter) {
        xf86EnableIOPorts(SvgaInfoRec.scrnIndex);

        vgaIOBase = (Inb(0x3CC) & 0x01) ? 0x3D0 : 0x3B0;

        /* Unprotect CRTC[0-7] */
        Outb(vgaIOBase + 4, 0x11); temp = Inb(vgaIOBase + 5);
        Outb(vgaIOBase + 5, temp & 0x7F);
    }
    else {
        /* Protect CRTC[0-7] */
        Outb(vgaIOBase + 4, 0x11); temp = Inb(vgaIOBase + 5);
        Outb(vgaIOBase + 5, (temp & 0x7F) | 0x80);

        xf86DisableIOPorts(SvgaInfoRec.scrnIndex);
   }
}


static void
GenericRestore(void *lpRes)
{
    vgaGenericPtr restore = (vgaGenericPtr)lpRes;

    vgaProtect(TRUE);
    vgaHWRestore((vgaHWPtr)restore);
    vgaProtect(FALSE);
}


static void *
GenericSave(void *lpSave)
{
    vgaGenericPtr save = (vgaGenericPtr)lpSave;
    save = (vgaGenericPtr)vgaHWSave((vgaHWPtr)save, sizeof(vgaGenericRec));

    return ((void *) save);
}

/*
 * GenericScrnInit --
 *
 * Sets some accelerated functions
 */
static int
GenericScrnInit(ScreenPtr pScreen, char *LinearBase, int virtualX,
            int virtualY, int res1, int res2, int width)
{
    return(TRUE);
}

static Boolean
GenericInit(DisplayModePtr mode)
{
    int i;

    if (!vgaHWInit(mode,sizeof(vgaGenericRec)))
        return(FALSE);

    /* We override all CRTC registers. */
    for (i = 0; i < 24; i++)
        new->std.CRTC[i] = vga320x200x256_CRTC[i];

    /* We only support one clock, 25 MHz, in a funny sort of way. */
    /* Always select clock 0 (25 MHz). */
    new->std.MiscOutReg = (new->std.MiscOutReg & 0x01) | 0x62;
#if 0
    /* Set bit 3 of SR1 (Divide dot clock by 2). */
    new->std.Sequencer[1] |= 0x08;
#endif

   vgaSetScreenInitHook((Boolean (*)())GenericScrnInit);

    return(TRUE);
}

static void
GenericFbInit()
{
}

static void
GenericAdjust(int x, int y)
{
    /* This isn't used. The best you would get is about 320x204 */
    /* (which doesn't work). */

    /* In standard VGA 320x200x256 (chain-4), the start address */
    /* is in pixel units. */
    int Base = (y * SvgaInfoRec.displayWidth + x);

    Outw(vgaIOBase + 4, (Base & 0x00FF00) | 0x0C);
      Outw(vgaIOBase + 4, ((Base & 0x00FF) << 8) | 0x0D);

#ifdef XFreeXDGA
    if (SvgaInfoRec.directMode & XF86DGADirectGraphics) {
        /* Wait until vertical retrace is in progress. */
        while (Inb(vgaIOBase + 0xA) & 0x08);
        while (!(Inb(vgaIOBase + 0xA) & 0x08));
    }
#endif
}

/*
 * GenericValidMode --
 *
 */
static int
GenericValidMode(DisplayModePtr mode, Boolean verbose, int flag)
{
    return MODE_OK;
}
